/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef ICM_REG_ACCESS_H
#define ICM_REG_ACCESS_H

/*!
 * @file icm_reg_access.h
 * @brief This file declares or defines icm register access functions
 */

/*******Includes***************************************************************/
#include "icm_drv.h"
#include "device_registers.h"

/*******Definitions************************************************************/

/*! @brief IPWM one channel sync ponit config width */
#define ADC_TRIGGER_SOURCE_CONFIG_WIDTH            (0x05UL)

/*! @brief SPWM fault source config width */
#define SPWM_FAULT_SOURCE_CONFIG_WIDTH             (0x02UL)

/*! @brief SPWM sync source config width */
#define SPWM_SYNC_SOURCE_CONFIG_WIDTH              (0x01UL)

/*! @brief SPWM0 modulation enable mask */
#define ICM_SPWM0_OUTSEL_MSK                       (0xFF0000UL)

/*! @brief SPWM1 modulation enable mask */
#define ICM_SPWM1_OUTSEL_MSK                       (0xFF000000UL)

/*! @brief IPWM capture cmp output config width */
#define ICM_IPWM_CAP_PAE_CONFIG_WIDTH              (0x2UL)

#if (CONFIG_ICM_SUPPORT_SPWM2_TRIGGER == 0U)
/*! @brief RTC clock capture mask */
#define ICM_RTC_CLK_CAPTURE_MSK                    (0x300UL)

/*! @brief LSI capture mask */
#define ICM_LSI_CAPTURE_MSK                        (0xC0UL)
#else
/*! @brief RTC clock capture mask */
#define ICM_RTC_CLK_CAPTURE_MSK                    (0x600UL)

/*! @brief LSI capture mask */
#define ICM_LSI_CAPTURE_MSK                        (0x180UL)
#endif
/*******APIs*******************************************************************/
#if defined(__cplusplus)
extern "C" {
#endif

/*!
 * @brief ADC normal channel trigger source select
 *
 * @param[in] regBase: The ICM register base address
 * @param[in] triggerTarget: ADC normal channel trigger target
 *            - 0: ICM_ADC0_NORMAL_CHANNEL_TRIGGER_SEL0
 *            - 1: ICM_ADC0_NORMAL_CHANNEL_TRIGGER_SEL1
 *            - 2: ICM_ADC0_NORMAL_CHANNEL_TRIGGER_SEL2
 *            - 3: ICM_ADC0_NORMAL_CHANNEL_TRIGGER_SEL3
 *            - 4: ICM_ADC1_NORMAL_CHANNEL_TRIGGER_SEL0
 *            - 5: ICM_ADC1_NORMAL_CHANNEL_TRIGGER_SEL1
 *            - 6: ICM_ADC1_NORMAL_CHANNEL_TRIGGER_SEL2
 *            - 7: ICM_ADC1_NORMAL_CHANNEL_TRIGGER_SEL3
 * @param[in] triggerSource: ADC trigger source
 * @return None
 */
static inline void ICM_REG_SetTriggerSourceForADCNormalChannel(ICM_Type* regBase,
                                                               icm_adc_normal_channel_trigger_target_t triggerTarget,
                                                               icm_adc_trigger_source_t triggerSource)
{
#if (CONFIG_ICM_SUPPORT_SPWM2_TRIGGER == 0U)
    uint32_t regValue = regBase->CR0;

    regValue &= ~(ICM_CR0_ADC0_NTRIG_SEL0_Msk << (triggerTarget * ADC_TRIGGER_SOURCE_CONFIG_WIDTH));
    regValue |= (uint32_t)triggerSource << (triggerTarget * ADC_TRIGGER_SOURCE_CONFIG_WIDTH);
    regBase->CR0 = regValue;
#else
    uint32_t regValue = 0U;

    if (triggerTarget < ICM_ADC1_NORMAL_CHANNEL_TRIGGER_SEL0) {
        regValue = regBase->CR0;
        regValue &= ~(ICM_CR0_ADC0_NTRIG_SEL0_Msk << (triggerTarget * ADC_TRIGGER_SOURCE_CONFIG_WIDTH));
        regValue |= (uint32_t)triggerSource << (triggerTarget * ADC_TRIGGER_SOURCE_CONFIG_WIDTH);
        regBase->CR0 = regValue;
    } else {
        regValue = regBase->CR2;
        triggerTarget = (icm_adc_normal_channel_trigger_target_t)(triggerTarget - ICM_ADC1_NORMAL_CHANNEL_TRIGGER_SEL0);
        regValue &= ~(ICM_CR2_ADC1_NTRIG_SEL0_Msk << (triggerTarget * ADC_TRIGGER_SOURCE_CONFIG_WIDTH));
        regValue |= (uint32_t)triggerSource << (triggerTarget * ADC_TRIGGER_SOURCE_CONFIG_WIDTH);
        regBase->CR2 = regValue;
    }

#endif
}

/*!
 * @brief ADC priority channel trigger source select
 *
 * @param[in] regBase: The ICM register base address
 * @param[in] triggerTarget: ADC priority channel trigger target
 *            - 0: ICM_ADC0_PRIORITY_CHANNEL_TRIGGER_SEL0
 *            - 1: ICM_ADC0_PRIORITY_CHANNEL_TRIGGER_SEL1
 *            - 2: ICM_ADC0_PRIORITY_CHANNEL_TRIGGER_SEL2
 *            - 3: ICM_ADC0_PRIORITY_CHANNEL_TRIGGER_SEL3
 *            - 4: ICM_ADC1_PRIORITY_CHANNEL_TRIGGER_SEL0
 *            - 5: ICM_ADC1_PRIORITY_CHANNEL_TRIGGER_SEL1
 *            - 6: ICM_ADC1_PRIORITY_CHANNEL_TRIGGER_SEL2
 *            - 7: ICM_ADC1_PRIORITY_CHANNEL_TRIGGER_SEL3
 * @param[in] triggerSource: ADC trigger source
 * @return None
 */
static inline void ICM_REG_SetTriggerSourceForADCPriorityChannel(ICM_Type* regBase,
                                                                 icm_adc_priority_channel_trigger_target_t triggerTarget,
                                                                 icm_adc_trigger_source_t triggerSource)
{
#if (CONFIG_ICM_SUPPORT_SPWM2_TRIGGER == 0U)
    uint32_t regValue = regBase->CR1;

    regValue &= ~(ICM_CR1_ADC0_PTRIG_SEL0_Msk << (triggerTarget * ADC_TRIGGER_SOURCE_CONFIG_WIDTH));
    regValue |= ((uint32_t)triggerSource << (triggerTarget * ADC_TRIGGER_SOURCE_CONFIG_WIDTH));
    regBase->CR1 = regValue;
#else
    uint32_t regValue = 0U;

    if (triggerTarget < ICM_ADC1_PRIORITY_CHANNEL_TRIGGER_SEL0) {
        regValue = regBase->CR1;
        regValue &= ~(ICM_CR1_ADC0_PTRIG_SEL0_Msk << (triggerTarget * ADC_TRIGGER_SOURCE_CONFIG_WIDTH));
        regValue |= ((uint32_t)triggerSource << (triggerTarget * ADC_TRIGGER_SOURCE_CONFIG_WIDTH));
        regBase->CR1 = regValue;
    } else {
        regValue = regBase->CR3;
        triggerTarget = (icm_adc_priority_channel_trigger_target_t)(triggerTarget - ICM_ADC1_PRIORITY_CHANNEL_TRIGGER_SEL0);
        regValue &= ~(ICM_CR3_ADC1_PTRIG_SEL0_Msk << (triggerTarget * ADC_TRIGGER_SOURCE_CONFIG_WIDTH));
        regValue |= ((uint32_t)triggerSource << (triggerTarget * ADC_TRIGGER_SOURCE_CONFIG_WIDTH));
        regBase->CR3 = regValue;
    }

#endif
}

/*!
 * @brief SPWM fault source select
 *
 * @param[in] regBase: The ICM register base address
 * @param[in] faultTarget: SPWM fault target
 *            - 0: ICM_SPWM0_FAULT2
 *            - 1: ICM_SPWM0_FAULT3
 *            - 2: ICM_SPWM1_FAULT2
 *            - 3: ICM_SPWM1_FAULT3
 *            - 4: ICM_SPWM2_FAULT2
 *            - 5: ICM_SPWM2_FAULT3
 * @param[in] faultSource: SPWM fault source
 *            - 0: ICM_FAULT_GND
 *            - 1: ICM_ADC_ANALOG_WATCHDOG
 *            - 2: ICM_ACMP_OUTPUT
 * @return None
 */
static inline void ICM_REG_SetSPWMFaultSource(ICM_Type* regBase, icm_spwm_fault_target_t faultTarget,
                                              icm_spwm_fault_source_t faultSource)
{
#if (CONFIG_ICM_SUPPORT_SPWM2_TRIGGER == 0U)
    uint32_t regValue = regBase->CR2;

    regValue &= ~(ICM_CR2_SPWM0_FAULT2_SEL_Msk << (faultTarget * SPWM_FAULT_SOURCE_CONFIG_WIDTH));
    regValue |= ((uint32_t)faultSource << (faultTarget * SPWM_FAULT_SOURCE_CONFIG_WIDTH));
    regBase->CR2 = regValue;
#else
    uint32_t regValue = regBase->CR4;

    regValue &= ~(ICM_CR4_SPWM0_FAULT2_SEL_Msk << (faultTarget * SPWM_FAULT_SOURCE_CONFIG_WIDTH));
    regValue |= ((uint32_t)faultSource << (faultTarget * SPWM_FAULT_SOURCE_CONFIG_WIDTH));
    regBase->CR4 = regValue;
#endif
}

/*!
 * @brief SPWM hardware sync source enable
 *
 * @param[in] regBase: The ICM register base address
 * @param[in] syncTarget: SPWM sync target
 *            - 0: ICM_SPWM0_SYNC0
 *            - 1: ICM_SPWM0_SYNC1
 *            - 2: ICM_SPWM1_SYNC0
 *            - 3: ICM_SPWM1_SYNC1
 *            - 4: ICM_SPWM2_SYNC0
 *            - 5: ICM_SPWM2_SYNC1
 * @param[in] enable
 *            - true: Enable
 *            - false: Disable
 * @return None
 */
static inline void ICM_REG_SetSPWMHardwareSyncSourceEn(ICM_Type* regBase, icm_spwm_sync_target_t syncTarget, bool enable)
{
#if (CONFIG_ICM_SUPPORT_SPWM2_TRIGGER == 0U)
    uint32_t regValue = regBase->CR2;

    regValue &= ~(ICM_CR2_SPWM0_SYNC0_SEL_Msk << (syncTarget * SPWM_SYNC_SOURCE_CONFIG_WIDTH));
    regValue |= ((uint32_t)enable << (ICM_CR2_SPWM0_SYNC0_SEL_Pos + syncTarget * SPWM_SYNC_SOURCE_CONFIG_WIDTH));
    regBase->CR2 = regValue;
#else
    uint32_t regValue = 0U;

    if (syncTarget < ICM_SPWM2_SYNC0) {
        regValue = regBase->CR4;
        regValue &= ~(ICM_CR4_SPWM0_SYNC0_SEL_Msk << (syncTarget * SPWM_SYNC_SOURCE_CONFIG_WIDTH));
        regValue |= ((uint32_t)enable << (ICM_CR4_SPWM0_SYNC0_SEL_Pos + syncTarget * SPWM_SYNC_SOURCE_CONFIG_WIDTH));
        regBase->CR4 = regValue;
    } else {
        regValue = regBase->CR5;
        syncTarget = (icm_spwm_sync_target_t)(syncTarget - ICM_SPWM2_SYNC0);
        regValue &= ~(ICM_CR5_SPWM2_SYNC0_SEL_Msk << (syncTarget * SPWM_SYNC_SOURCE_CONFIG_WIDTH));
        regValue |= ((uint32_t)enable << (ICM_CR5_SPWM2_SYNC0_SEL_Pos + syncTarget * SPWM_SYNC_SOURCE_CONFIG_WIDTH));
        regBase->CR5 = regValue;
    }

#endif
}

/*!
 * @brief SPWM0 modulation enable
 *
 * @param[in] regBase: The ICM register base address
 * @param[in] modulationMsk: SPWM0 CH0~7 modulation enable mask
 * @return None
 */
static inline void ICM_REG_SetSPWM0ModulationEn(ICM_Type* regBase, uint8_t modulationMsk)
{
#if (CONFIG_ICM_SUPPORT_SPWM2_TRIGGER == 0U)
    uint32_t regValue = regBase->CR2;

    regValue &= ~ICM_SPWM0_OUTSEL_MSK;
    regValue |= ((uint32_t)modulationMsk << ICM_CR2_SPWM0_OUT0_SEL_Pos);
    regBase->CR2 = regValue;
#else
    uint32_t regValue = regBase->CR4;

    regValue &= ~ICM_SPWM0_OUTSEL_MSK;
    regValue |= ((uint32_t)modulationMsk << ICM_CR4_SPWM0_OUT0_SEL_Pos);
    regBase->CR4 = regValue;
#endif
}

/*!
 * @brief SPWM1 modulation enable
 *
 * @param[in] regBase: The ICM register base address
 * @param[in] modulationMsk: SPWM1 CH0~7 modulation enable mask
 * @return None
 */
static inline void ICM_REG_SetSPWM1ModulationEn(ICM_Type* regBase, uint8_t modulationMsk)
{
#if (CONFIG_ICM_SUPPORT_SPWM2_TRIGGER == 0U)
    uint32_t regValue = regBase->CR2;

    regValue &= ~ICM_SPWM1_OUTSEL_MSK;
    regValue |= ((uint32_t)modulationMsk << ICM_CR2_SPWM1_OUT0_SEL_Pos);
    regBase->CR2 = regValue;
#else
    uint32_t regValue = regBase->CR4;

    regValue &= ~ICM_SPWM1_OUTSEL_MSK;
    regValue |= ((uint32_t)modulationMsk << ICM_CR4_SPWM1_OUT0_SEL_Pos);
    regBase->CR4 = regValue;
#endif
}

/*!
 * @brief CMP parallel output capture by SPWM enable
 *
 * @param[in] regBase: The ICM register base address
 * @param[in] target: CMP parallel output capture by spwm target
 *            - 0: ICM_SPWM0_CAPTURE
 *            - 1: ICM_SPWM1_CAPTURE
 *            - 2: ICM_SPWM2_CAPTURE
 * @param[in] enable
 *            - true: Enable
 *            - false: Disable
 * @return None
 */
static inline void ICM_REG_SetSPWMCapturePOSEn(ICM_Type* regBase, icm_spwm_capture_pos_target_t target, bool enable)
{
#if (CONFIG_ICM_SUPPORT_SPWM2_TRIGGER == 0U)
    uint32_t regValue = regBase->CR3;

    regValue &= ~(ICM_CR3_SPWM0_CAP_PAE_Msk << (ICM_CR3_SPWM0_CAP_PAE_Pos + target));
    regValue |= ((uint32_t)enable << (ICM_CR3_SPWM0_CAP_PAE_Pos + target));
    regBase->CR3 = regValue;
#else
    uint32_t regValue = regBase->CR5;

    regValue &= ~(ICM_CR5_SPWM0_CAP_PAE_Msk << (ICM_CR5_SPWM0_CAP_PAE_Pos + target));
    regValue |= ((uint32_t)enable << (ICM_CR5_SPWM0_CAP_PAE_Pos + target));
    regBase->CR5 = regValue;
#endif
}

/*!
 * @brief Set IPWM capture source and capture target from CMP
 *
 * @param[in] regBase: The ICM register base address
 * @param[in] triggerTarget: IPWM capture target
 *            - 0: ICM_IPWM0_CH0_CAPTURE
 *            - 1: ICM_IPWM0_CH1_CAPTURE
 *            - 2: ICM_IPWM0_CH2_CAPTURE
 *            - 3: ICM_IPWM0_CH3_CAPTURE
 *            - 4: ICM_IPWM1_CH0_CAPTURE
 *            - 5: ICM_IPWM1_CH1_CAPTURE
 *            - 6: ICM_IPWM1_CH2_CAPTURE
 *            - 7: ICM_IPWM1_CH3_CAPTURE
 * @param[in] source: IPWM capture source
 *            - 0: ICM_CONNECT_PAD
 *            - 1: ICM_CMP_PARALLEL_OUTPUT
 *            - 2: ICM_CMP_SERIAL_OUTPUT
 * @return None
 */
static inline void ICM_REG_SetIPWMCaptureSource(ICM_Type* regBase, icm_cmp_output_trigger_target_t target, icm_cmp_output_source_t source)
{
#if (CONFIG_ICM_SUPPORT_SPWM2_TRIGGER == 0U)
    uint32_t regValue = regBase->CR3;

    regValue &= ~(ICM_CR3_IPWM0_CAP0_SEL_Msk << (ICM_IPWM_CAP_PAE_CONFIG_WIDTH * target));
    regValue |= ((uint32_t)source << (ICM_CR3_IPWM0_CAP0_SEL_Pos + ICM_IPWM_CAP_PAE_CONFIG_WIDTH * target));
    regBase->CR3 = regValue;
#else
    uint32_t regValue = regBase->CR5;

    regValue &= ~(ICM_CR5_IPWM0_CAP0_SEL_Msk << (ICM_IPWM_CAP_PAE_CONFIG_WIDTH * target));
    regValue |= ((uint32_t)source << (ICM_CR5_IPWM0_CAP0_SEL_Pos + ICM_IPWM_CAP_PAE_CONFIG_WIDTH * target));
    regBase->CR5 = regValue;
#endif
}

/*!
 * @brief LSI capture enable
 *
 * @param[in] regBase: The ICM register base address
 * @param[in] enable
 *            - true: LSI capture enable
 *            - false: LSI capture disable
 * @return None
 */
static inline void ICM_REG_SetLSICaptureEn(ICM_Type* regBase, bool enable)
{
#if (CONFIG_ICM_SUPPORT_SPWM2_TRIGGER == 0U)
    uint32_t regValue = regBase->CR3;

    regValue &= ~ICM_CR3_IPWM0_CAP2_SEL_Msk;

    if (enable) {
        regValue |= ICM_LSI_CAPTURE_MSK;
    }

    regBase->CR3 = regValue;
#else
    uint32_t regValue = regBase->CR5;

    regValue &= ~ICM_CR5_IPWM0_CAP2_SEL_Msk;

    if (enable) {
        regValue |= ICM_LSI_CAPTURE_MSK;
    }

    regBase->CR5 = regValue;
#endif
}

/*!
 * @brief RTC clock capture enable
 *
 * @param[in] regBase: The ICM register base address
 * @param[in] enable
 *            - true: RTC clock capture enable
 *            - false: RTC clock capture disable
 * @return None
 */
static inline void ICM_REG_SetRTCClockCaptureEn(ICM_Type* regBase, bool enable)
{
#if (CONFIG_ICM_SUPPORT_SPWM2_TRIGGER == 0U)
    uint32_t regValue = regBase->CR3;

    regValue &= ~ICM_CR3_IPWM0_CAP3_SEL_Msk;

    if (enable) {
        regValue |= ICM_RTC_CLK_CAPTURE_MSK;
    }

    regBase->CR3 = regValue;
#else
    uint32_t regValue = regBase->CR5;

    regValue &= ~ICM_CR5_IPWM0_CAP3_SEL_Msk;

    if (enable) {
        regValue |= ICM_RTC_CLK_CAPTURE_MSK;
    }

    regBase->CR5 = regValue;
#endif
}

/*!
 * @brief CMP window source select
 *
 * @param[in] regBase: The ICM register base address
 * @param[in] source: CMP window source
 *            - 0: ICM_WINDOW_IPWM0_CH0
 *            - 1: ICM_WINDOW_IPWM0_CH1
 *            - 2: ICM_WINDOW_IPWM0_CH2
 *            - 3: ICM_WINDOW_IPWM0_CH3
 * @return None
 */
static inline void ICM_REG_SetCMPWindowSource(ICM_Type* regBase, icm_cmp_window_source_t source)
{
#if (CONFIG_ICM_SUPPORT_SPWM2_TRIGGER == 0U)
    uint32_t regValue = regBase->CR3;

    regValue &= ~ICM_CR3_CMP_WINDOW_SEL_Msk;
    regValue |= ((uint32_t)source << ICM_CR3_CMP_WINDOW_SEL_Pos);
    regBase->CR3 = regValue;
#else
    uint32_t regValue = regBase->CR5;

    regValue &= ~ICM_CR5_CMP_WINDOW_SEL_Msk;
    regValue |= ((uint32_t)source << ICM_CR5_CMP_WINDOW_SEL_Pos);
    regBase->CR5 = regValue;
#endif
}

#if defined(__cplusplus)
}
#endif

#endif /* ICM_REG_ACCESS_H */

/*******EOF********************************************************************/

